/*
 * main.cpp
 *
 *  Created on: 17.09.2013
 *      Author: dima
 */

#include "Model/GameObjectModel.hpp"
#include "Model/PlayerModel.hpp"
#include "Model/OpponentModel.hpp"
#include "Model/AI.hpp"
#include "Model/MapModel.hpp"
#include "Model/Types.hpp"

#include "View/TankView.hpp"
#include "View/MissileView.hpp"
#include "View/MapView.hpp"

#include "Resources/IMGLoader.hpp"
#include "Resources/IMGSplitter.hpp"
#include "Resources/SoundLoader.hpp"
#include "Resources/MapParser.hpp"

#include "Controller/InputHandler.hpp"

#include "Game.hpp"
#include "Globals.hpp"

#include <SDL/SDL.h>
#include <SDL/SDL_main.h>
#include <SDL/SDL_mixer.h>
#include <SDL/SDL_image.h>
#include <map>
#include <iostream>



int main(int argc, char * argv[])
{
  if(argc <= 1)
  { // no map to load
    return 0;
  }

#ifdef WINDOWS
  // SDL redirects stdout and stderr to txt files
  // redirect them to console again
  //freopen("CON", "w", stdout);
  //freopen("CON", "w", stderr);
#endif

  std::string myself = std::string(argv[0]);
  std::string myDir = "";
  unsigned int position = myself.rfind(Tanks::Globals::directorySplitter);
  if(position != std::string::npos)
  {
    myDir = myself.substr(0, position) + Tanks::Globals::directorySplitter;
  }
  std::string mapFilename = std::string(argv[1]);

  try
  {
    Tanks::Game game;
    game.Initialize(myDir);
    game.LoadMap(mapFilename);
    game.Run();
  }
  catch(std::string & e)
  {
    std::cout << e;
    std::cout.flush();
  }
  return 0;
}


Tanks::
Game::
Game() :
  m_screen(0),
  m_myDir(""),
  m_mapModel(0),
  m_mapView(0){}


Tanks::
Game::
~Game()
{
  SDL_Quit();
}


void
Tanks::
Game::
Initialize(std::string argMyDir)
{
  m_myDir = argMyDir;
  if(SDL_Init(SDL_INIT_EVERYTHING) == -1)
  {
    throw std::string("Could not initialize SDL");
  }

  m_screen = SDL_SetVideoMode(Globals::screenWidth,
                              Globals::screenHeight,
                              Globals::screenBPP,
                              SDL_SWSURFACE);
  if(m_screen == NULL)
  {
    throw std::string("Could not create screen");
  }
  SDL_WM_SetCaption("Tanks", NULL);

  if(Mix_OpenAudio(Globals::samplingFrequency,
                   Globals::audioFormat,
                   Globals::numChannels,
                   Globals::chunkSize) == -1)
  {
    std::cout << "Could not initialize audio stream. The game will mute." << std::endl;
  }

  Resources::IMGLoader & imgLoader = Resources::IMGLoader::getInstance();
  imgLoader.Load(m_myDir);

  Resources::IMGSplitter & imgSplitter = Resources::IMGSplitter::getInstance();
  imgSplitter.Split();

  Resources::SoundLoader & soundLoader = Resources::SoundLoader::getInstance();
  soundLoader.Load(m_myDir);
}


void
Tanks::
Game::
LoadMap(std::string argMapFilename)
{
  m_mapModel = new Tanks::Model::MapModel;
  m_mapView = new Tanks::View::MapView(m_mapModel, m_screen);

  Resources::IMGLoader & imgLoader = Resources::IMGLoader::getInstance();
  Tanks::Resources::MapParser parser(Globals::screenWidth, Globals::screenHeight, m_myDir, argMapFilename);
  parser.Parse();

  for(int i = 0; i < parser.GetNumberOfMapObjects(); ++i)
  {
    Tanks::Resources::MapParser::MapObject * mapObject
      = parser.GetMapObject(i);

    std::string imagePath = m_myDir + "Images" + Globals::directorySplitter + mapObject->m_imageFilename;
    SDL_Surface * image = imgLoader.LoadImage(imagePath, false);
    if(image != NULL)
    {
      m_mapView->AddImage(i, image);
    }
    else
    {
      continue;
    }

    Tanks::Model::GameObjectType type = mapObject->m_type;
    switch(type)
    {
    case Tanks::Model::SolidBlock:
    {
      Tanks::Model::SolidBlockModel * solidBlock
        = new Tanks::Model::SolidBlockModel(mapObject->m_rect.x,
                                            mapObject->m_rect.y,
                                            mapObject->m_rect.w,
                                            mapObject->m_rect.h);
      m_mapModel->AddGameObject(solidBlock, i);
      break;
    }
    case Tanks::Model::Land:
    {
      Tanks::Model::LandModel * land
        = new Tanks::Model::LandModel(mapObject->m_rect.x,
                                      mapObject->m_rect.y,
                                      mapObject->m_rect.w,
                                      mapObject->m_rect.h);
      m_mapModel->AddGameObject(land, i);
      break;
    }
    default:
      throw std::string("Game::LoadMap(): Tried to create not valid block");
      break;
    }
  }
}


void
Tanks::
Game::
Run()
{
  if(m_mapView == 0) throw std::string("No map to play on");

  int requiredDelay = (int)(1000/Globals::FPS);

  Tanks::Model::PlayerModel player1Model(10, 10, 70, 70);
  Tanks::Controller::InputHandler player1Input(&player1Model);
  Tanks::View::TankView player1View(&player1Model, m_screen);

  Tanks::Model::OpponentModel opponent1(300, 300, 70, 70, Tanks::Model::Easy);
  Tanks::Model::AI ki1(&opponent1);
  Tanks::View::TankView opponentView1(&opponent1, m_screen);

  Tanks::Model::OpponentModel opponent2(420, 300, 70, 70, Tanks::Model::Medium);
  Tanks::Model::AI ki2(&opponent2);
  Tanks::View::TankView opponentView2(&opponent2, m_screen);

  Tanks::Model::OpponentModel opponent3(540, 300, 70, 70, Tanks::Model::Hard);
  Tanks::Model::AI ki3(&opponent3);
  Tanks::View::TankView opponentView3(&opponent3, m_screen);

  Tanks::View::MissileView missileView(m_screen);

  bool run = true;

  std::map<int, int> histo;
  while(run)
  {
    int time = SDL_GetTicks();
    //handleInput returns false, iff Quit command was requested
    run = player1Input.handleInput();
    ki1.Tick();
    ki2.Tick();
    ki3.Tick();
    player1Model.Tick();
    opponent1.Tick();
    opponent2.Tick();
    opponent3.Tick();
    std::map<Tanks::Model::MissileModel *, Tanks::Model::TankModel *>::iterator missileIter;
    for(missileIter = Tanks::Model::MissileModel::Begin();
        missileIter != Tanks::Model::MissileModel::End(); ++missileIter)
    {
      missileIter->first->Tick();
    }

    m_mapView->Draw();

    // Draw Tank
    opponentView1.Draw();
    opponentView2.Draw();
    opponentView3.Draw();
    player1View.Draw();

    missileView.Draw();

    SDL_Flip(m_screen);

    int lateness = SDL_GetTicks() - time;
    int remainingDelay = requiredDelay - lateness;
    histo[remainingDelay]++;
    if(remainingDelay > 0)
    { SDL_Delay(remainingDelay); }
  }
  std::map<int, int>::iterator histoIter;
  for(histoIter = histo.begin(); histoIter != histo.end(); ++histoIter)
  { std::cout << histoIter->first << " - " << histoIter->second << std::endl; }
}

